package uy.org.softtech.cssuite.web.comando;

import static org.apache.commons.lang.StringUtils.lastIndexOfAny;
import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;
import static org.springframework.util.StringUtils.hasText;
import static uy.org.softtech.cssuite.entidad.cliente.Cliente.Tipo.EMPRESA;
import static uy.org.softtech.cssuite.entidad.cliente.Cliente.Tipo.PERSONA;
import static uy.org.softtech.cssuite.helpers.HelperTextos.formatearTelefono;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaCelular;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaCorreoElectronico;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaNombre;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaNumeroRut;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaRazonSocial;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaTelefono;
import static uy.org.softtech.cssuite.helpers.HelperTextos.validaUrl;

import java.util.Locale;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import uy.org.softtech.cssuite.core.Comando;
import uy.org.softtech.cssuite.core.Controlador.Accion;
import uy.org.softtech.cssuite.core.Modelo;
import uy.org.softtech.cssuite.entidad.cliente.Cliente;
import uy.org.softtech.cssuite.entidad.cliente.Empresa;
import uy.org.softtech.cssuite.entidad.cliente.Persona;
import uy.org.softtech.cssuite.excepciones.EntidadNoActualizadaException;
import uy.org.softtech.cssuite.excepciones.EntidadNoCreadaException;
import uy.org.softtech.cssuite.excepciones.EntidadNoEliminadaException;
import uy.org.softtech.cssuite.excepciones.EntidadNoEncontradaException;
import uy.org.softtech.cssuite.servicios.ServiciosClientes;
import uy.org.softtech.cssuite.web.modelo.ModeloCliente;

/**
 * Implementación de {@link Comando} encargado de procesar la solicitud de creacion y/o actualización de la información de un cliente
 * 
 * @author fabian.lobo
 * @see Comando#ejecutar(Modelo, BindingResult)
 */
@Component("comando-cliente")
public class ComandoCliente extends Comando {
	
	private Logger log = Logger.getLogger(getClass());
	
	// Servicios
	@Resource(name = "servicios-clientes" )
	private ServiciosClientes serviciosClientes;
	
	/**
	 * Método para verificar que todos los parámetros han sido seteados correctamente.
	 * 
	 * @author fabian.lobo
	 */
	@PostConstruct
	public void inicializacion(){
		super.inicializacion();
		
		notNull(serviciosClientes(), "La propiedad [serviciosClientes] de [" + getClass().getName() + "] no ha sido establecida.");
		
		log.info("Comando encargado de procesar la solicitud de actualización de la información de un usuario ha sido inicializado.");
	}

	@Override
	public void validaModelo(Modelo modelo, BindingResult errores) {
		notNull(modelo, "El modelo no puede ser nulo.");
		isTrue(modelo instanceof ModeloCliente, "El modelo no es el correcto para este comando.");
		
		ModeloCliente modeloCliente = (ModeloCliente) modelo;
		
		notNull(modeloCliente.getAccion(), "Accion no puede ser nulo en este modelo.");
		notNull(modeloCliente.getLocale(), "Locale no puede ser nulo en este modelo.");
		
		Locale locale = modeloCliente.getLocale();
		Accion accion = modeloCliente.getAccion();
		
		//CAMPOS NO EDITABLES -> ID
		switch(accion) {
		case ACTUALIZAR:
		case BLOQUEAR:
		case DESBLOQUEAR:
		case ELIMINAR:
			notNull(modeloCliente.getId(), "ID no puede ser nulo en este modelo.");
			break;
		}
		
		String tipo = modeloCliente.getTipo();
		String nombre = modeloCliente.getNombre();
		String[] tipos = new String[]{ 
			PERSONA.toString(), 
			EMPRESA.toString()
		};
				
		switch(accion) {
		case CREAR:
			if(hasText(tipo) || lastIndexOfAny(tipo, tipos) > 0) {
				if(hasText(nombre)) {
					if (!validaNombre(nombre)) {
				    	//El nombre contiene caracteres extraños
						errores.addError(new FieldError("modeloCliente", "nombre", fuenteMensajes().getMessage("mensaje.textoInvalido", null, "El texto ingresado contiene caracteres que no corresponden.", locale)));
				    }
				} else {
					//El nombre no puede ser nulo o espacios en blanco
					errores.addError(new FieldError("modeloCliente", "nombre", fuenteMensajes().getMessage("mensaje.nombreRequerido", null, "Por favor, ingrese el nombre.", locale)));
				}
				
				//Persona
				if(tipo.equalsIgnoreCase(PERSONA.toString())) {
					
					String apellido = modeloCliente.getApellido();
					String correoElectronico = modeloCliente.getCorreoElectronico();
					String segundoNombre = modeloCliente.getSegundoNombre();
					String telefono = modeloCliente.getTelefono();
					String celular = modeloCliente.getCelular();
					
					if(hasText(apellido)) {
						if (!validaNombre(apellido)) {
					    	//El apellido contiene caracteres extraños
							errores.addError(new FieldError("modeloCliente", "apellido", fuenteMensajes().getMessage("mensaje.textoInvalido", null, "El texto ingresado contiene caracteres que no corresponden.", locale)));
					    }
					} else {
						//El apellido no puede ser nulo o espacios en blanco
						errores.addError(new FieldError("modeloCliente", "apellido", fuenteMensajes().getMessage("mensaje.apellidoRequerido", null, "Por favor, ingrese el apellido.", locale)));
					}
					if(hasText(correoElectronico)) {
						if(!validaCorreoElectronico(correoElectronico)) {
							//Debe ser una dirección de correo electrónico
							errores.addError(new FieldError("modeloCliente", "correoElectronico", fuenteMensajes().getMessage("mensaje.correoElectronicoInvalido", null, "Por favor, ingrese un correo electrónico válido.", locale)));
						}
					} else {
						//El correo electrónico no puede ser nulo o espacios en blanco
						errores.addError(new FieldError("modeloCliente", "correoElectronico", fuenteMensajes().getMessage("mensaje.correoElectronicoRequerido", null, "Por favor, ingrese un correo electrónico.", locale)));
					}
					if(hasText(segundoNombre)) {
						if (!validaNombre(segundoNombre)) {
					    	//El segundo nombre contiene caracteres extraños
							errores.addError(new FieldError("modeloCliente", "segundoNombre", fuenteMensajes().getMessage("mensaje.textoInvalido", null, "El texto ingresado contiene caracteres que no corresponden.", locale)));
					    }
					}
					if(hasText(telefono)) {
						if(!validaTelefono(telefono, locale)) {
							//Debe ser teléfono fijo válido
							errores.addError(new FieldError("modeloCliente", "telefono", fuenteMensajes().getMessage("mensaje.telefonoFijoInvalido", null, "Por favor, ingrese un número de teléfono fijo válido.", locale)));
						}
					}
					if(hasText(celular)) {
						if(!validaCelular(celular, locale)) {
							//Debe ser teléfono celular válido
							errores.addError(new FieldError("modeloCliente", "celular", fuenteMensajes().getMessage("mensaje.telefonoCelularInvalido", null, "Por favor, ingrese un número de teléfono móvil válido.", locale)));
						}
					}
				}
				
				//Empresa
				if(tipo.equalsIgnoreCase(EMPRESA.toString())) {
					
					String razonSocial = modeloCliente.getRazonSocial();
					String rut = modeloCliente.getRut();
					String url = modeloCliente.getUrl();
					
					if(hasText(razonSocial)) {
						if(!validaRazonSocial(razonSocial)) {
							//La razón social no es válida
							errores.addError(new FieldError("modeloCliente", "razonSocial", fuenteMensajes().getMessage("mensaje.razonSocialInvalida", null, "Por favor, ingrese una razón social válida.", locale)));
						}
					} else {
						//La razón social no puede ser nulo o espacios en blanco
						errores.addError(new FieldError("modeloCliente", "razonSocial", fuenteMensajes().getMessage("mensaje.razonSocialRequerida", null, "Por favor, ingrese la razón social.", locale)));
					}
					if(hasText(rut)) {
						if(!validaNumeroRut(rut)) {
							//El número de RUT no es válido
							errores.addError(new FieldError("modeloCliente", "rut", fuenteMensajes().getMessage("mensaje.numeroRutInvalido", null, "Por favor, ingrese un número de registro único tributario válido.", locale)));
						}
					} else {
						//El número de RUT no puede ser nulo o espacios en blanco
						errores.addError(new FieldError("modeloCliente", "rut", fuenteMensajes().getMessage("mensaje.numeroRutRequerido", null, "Por favor, ingrese el número de registro único tributario de la empresa.", locale)));
					}
					if(hasText(url)) {
						if (!validaUrl(url)) {
					    	//La URL debe ser una URL válida
							errores.addError(new FieldError("modeloCliente", "url", fuenteMensajes().getMessage("mensaje.urlInvalida", null, "Por favor, ingrese una URL válida.", locale)));
					    }
					}
				}
			} else {
				//Debe seleccionar un tipo de cliente
				errores.addError(new FieldError("modeloCliente", "tipo", fuenteMensajes().getMessage("mensaje.tipoClienteRequerido", null, "Por favor, seleccion un tipo de cliente.", locale)));
			}
			
			break;
		case ACTUALIZAR:
			
			break;
		}
	}

	@Override
	public void ejecutarComando(Modelo modelo, BindingResult errores) {
		notNull(modelo, "El modelo no puede ser nulo.");
		isTrue(modelo instanceof ModeloCliente, "El modelo no es el correcto para este comando.");
		
		ModeloCliente modeloCliente = (ModeloCliente) modelo;
		
		notNull(modeloCliente.getAccion(), "Accion no puede ser nulo en este modelo.");
		notNull(modeloCliente.getLocale(), "Locale no puede ser nulo en este modelo.");
		
		Locale locale = modeloCliente.getLocale();
		Accion accion = modeloCliente.getAccion();
		
		//CAMPOS NO EDITABLES -> ID
		switch(accion) {
		case ACTUALIZAR:
		case BLOQUEAR:
		case DESBLOQUEAR:
		case ELIMINAR:
			notNull(modeloCliente.getId(), "ID no puede ser nulo en este modelo.");
			break;
		}
		
		Cliente cliente = null;
		switch(accion) {
		case BLOQUEAR:
			try {
				serviciosClientes().bloquearCliente(modeloCliente.getId());
			} catch (EntidadNoEncontradaException e) {
				log.error(e);
				errores.addError(new ObjectError("modeloCliente", e.getMessage()));
			} catch (EntidadNoActualizadaException e) {
				log.error(e);
				errores.addError(new ObjectError("modeloCliente", e.getMessage()));
			}
			break;
		case DESBLOQUEAR:
			try {
				serviciosClientes().desbloquearCliente(modeloCliente.getId());
			} catch (EntidadNoEncontradaException e) {
				log.error(e);
				errores.addError(new ObjectError("modeloCliente", e.getMessage()));
			} catch (EntidadNoActualizadaException e) {
				log.error(e);
				errores.addError(new ObjectError("modeloCliente", e.getMessage()));
			}
			break;
		case ELIMINAR:
			try {
				serviciosClientes().eliminarCliente(modeloCliente.getId());
			} catch (EntidadNoEncontradaException e) {
				log.error(e);
				errores.addError(new ObjectError("modeloCliente", e.getMessage()));
			} catch (EntidadNoEliminadaException e) {
				log.error(e);
				errores.addError(new ObjectError("modeloCliente", e.getMessage()));
			}
			break;
		case CREAR:
			String tipo = modeloCliente.getTipo();
			
			//Persona
			if(tipo.equalsIgnoreCase(PERSONA.toString())) {
				cliente = new Persona();
				((Persona) cliente).setPrimerNombre(modeloCliente.getNombre());
				((Persona) cliente).setApellido(modeloCliente.getApellido());
				((Persona) cliente).setCorreoElectronico(modeloCliente.getCorreoElectronico());
				((Persona) cliente).setSegundoNombre(modeloCliente.getSegundoNombre());
				((Persona) cliente).setTelefono(modeloCliente.getTelefono());
				((Persona) cliente).setCelular(modeloCliente.getCelular());
			}
			
			//Empresa
			if(tipo.equalsIgnoreCase(EMPRESA.toString())) {
				cliente = new Empresa();
				((Empresa) cliente).setNombre(modeloCliente.getNombre());
				((Empresa) cliente).setRazonSocial(modeloCliente.getRazonSocial());
				((Empresa) cliente).setRut(modeloCliente.getRut());
				((Empresa) cliente).setUrl(modeloCliente.getUrl());
			}
			
			cliente.setComentarios(modeloCliente.getComentarios());
			
			try {
				serviciosClientes().crear(cliente, SERVICIO);
			} catch (EntidadNoCreadaException ence) {
				log.error(ence);
				errores.addError(new ObjectError("modeloCliente", fuenteMensajes().getMessage("mensaje.datosNoGuardados", null, "No hemos podido almacenar los datos. Por favor, contacte al administrador del sistema.", locale)));
			}
			break;
		case ACTUALIZAR:
			
			try {
				cliente = serviciosClientes().buscarCliente(modeloCliente.getId());
				cliente.setComentarios(modeloCliente.getComentarios());
				
				switch(cliente.getTipo()) {
				case PERSONA:
					((Persona) cliente).setPrimerNombre(modeloCliente.getNombre());
					((Persona) cliente).setApellido(modeloCliente.getApellido());
					((Persona) cliente).setSegundoNombre(modeloCliente.getSegundoNombre());
					((Persona) cliente).setCorreoElectronico(modeloCliente.getCorreoElectronico());
					((Persona) cliente).setTelefono(formatearTelefono(modeloCliente.getTelefono(), locale));
					((Persona) cliente).setCelular(formatearTelefono(modeloCliente.getCelular(), locale));
					break;
				case EMPRESA:
					((Empresa) cliente).setNombre(modeloCliente.getNombre());
					((Empresa) cliente).setRazonSocial(modeloCliente.getRazonSocial());
					((Empresa) cliente).setRut(modeloCliente.getRut());
					((Empresa) cliente).setUrl(modeloCliente.getUrl());
					break;
				}
				serviciosClientes().actualizar(cliente, SERVICIO);
			} catch (EntidadNoEncontradaException enee) {
				log.error(enee);
				errores.addError(new ObjectError("modeloCliente", fuenteMensajes().getMessage("mensaje.datosNoModificados", null, "No hemos podido actualizar los datos. Por favor, contacte al administrador del sistema.", locale)));
			} catch (EntidadNoActualizadaException enae) {
				log.error(enae);
				errores.addError(new ObjectError("modeloCliente", fuenteMensajes().getMessage("mensaje.datosNoModificados", null, "No hemos podido actualizar los datos. Por favor, contacte al administrador del sistema.", locale)));
			}
			break;
		}
	}
	
	/*
	 *  GETTERS
	 */
	public ServiciosClientes serviciosClientes() {
		return serviciosClientes;
	}
}
